---
title: 권장사항(DO/DON'T)
version: 1
---

import { Link } from "/src/components/Link";
import { AutoSnippet, When } from "/src/components/CodeSnippet";

코드의 유지보수성을 높이기 위해 Riverpod를 사용할 때 따라야 할 모범 사례 목록은 다음과 같습니다.

이 목록은 전체 목록이 아니며 변경될 수 있습니다.
제안 사항이 있으면 언제든지 [이슈를 열어주세요](https://github.com/rrousselGit/riverpod/issues/new?assignees=rrousselGit&labels=documentation%2C+needs+triage&projects=&template=example_request.md&title=).

이 목록의 항목은 특별한 순서가 정해져 있지 않습니다.

이러한 권장 사항의 상당 부분은 [riverpod_lint](https://pub.dev/packages/riverpod_lint)로 적용할 수 있습니다.
설치 지침은 <Link documentID="introduction/getting_started" hash="enabling-riverpod_lintcustom_lint"/>를 참조하세요.

## AVOID 위젯에서 provider를 초기화하지 마세요

providers는 스스로 초기화해야 합니다.  
위젯과 같은 외부 요소에 의해 초기화되어서는 안 됩니다.

그렇게 하지 않으면 경합 상태(race conditions) 및 예기치 않은 동작(unexpected behaviors)이 발생할 수 있습니다.

**DON'T**

```dart
class WidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    // Bad: provider가 자체적으로 초기화해야 합니다.
    ref.read(provider).init();
  }
}
```

**CONSIDER**

이 문제에 대한 "모든 것에 맞는" 해결책은 없습니다.  
초기화 로직이 provider 외부의 요인에 따라 달라지는 경우 이러한 로직을 배치하는 올바른 위치는 탐색(navigation)을 트리거하는 버튼의 `onPressed` 메서드에 있는 경우가 많습니다:

```dart
ElevatedButton(
  onPressed: () {
    ref.read(provider).init();
    Navigator.of(context).push(...);
  },
  child: Text('Navigate'),
)
```

## AVOID 로컬 위젯 상태에 provider를 사용하지 마세요.

Provider는 공유 비즈니스 상태(shared business state)용으로 설계되었습니다.
로컬 위젯 상태와 같은 용도로는 사용하기에 적합하지 않습니다:

- 양식(form) 상태 저장
- 현재 선택된 항목
- 애니메이션
- 일반적으로 Flutter가 "controller"(예: `TextEditingController`)로 처리하는 모든 것

로컬 위젯 상태를 처리하는 방법을 찾고 있다면 대신 [flutter_hooks](https://pub.dev/packages/flutter_hooks)를 사용하는 것이 좋습니다.

이를 권장하지 않는 이유 중 하나는 이러한 상태가 경로로 범위가 지정(scoped to a route)되는 경우가 많기 때문입니다.  
그렇게 하지 않으면 새 페이지가 이전 페이지의 상태를 재정의하기 때문에 앱의 뒤로 가기 버튼이 손상될 수 있습니다.

## DON'T provider를 초기화하는 동안 부가작업(side effects)을 수행하지 마세요.

provider는 일반적으로 "read" 작업을 나타내는 데 사용해야 합니다.
양식 제출과 같은 "write" 작업에는 사용하지 않아야 합니다.

이러한 작업에 provider를 사용하면, 이전에 수행된 부가작업을 건너뛰는 등 예기치 않은 동작이 발생할 수 있습니다.

부가작업의 로딩/오류 상태를 처리하는 방법을 알아보려면 <Link documentID="essentials/side_effects"/>을 참조하세요.

**DON'T**:

```dart
final submitProvider = FutureProvider((ref) async {
  final formState = ref.watch(formState);

  // Bad: providers는 'write' 작업에 사용해서는 안 됩니다.
  return http.post('https://my-api.com', body: formState.toJson());
});
```

## PREFER 정적으로 알려진 providers를 사용하여 ref.watch/read/listen (및 유사한 API)를 호출하세요.

Riverpod은 린트 규칙을 활성화할 것을 강력히 권장합니다(`riverpod_lint`를 통해).  
하지만 린트를 효과적으로 사용하려면 코드를 정적으로 분석할 수 있는 방식으로 작성해야 합니다.

그렇게 하지 않으면 버그를 발견하기가 더 어려워지거나 린트로 오탐이 발생할 수 있습니다.

**Do**:

```dart
final provider = Provider((ref) => 42);

...

// OK provider가 정적으로 알려져 있으므로 확인 가능
ref.watch(provider);
```

**Don't**:

```dart
class Example extends ConsumerWidget {
  Example({required this.provider});
  final Provider<int> provider;

  @override
  Widget build(context, ref) {
    // Bad 정적 분석이 `provider`가 무엇인지 알 수 없으므로 나쁨
    ref.watch(provider);
  }
}
```

## AVOID 동적으로 providers 생성하지 않기

providers는 최상위 레벨(top-level)의 final 변수만 사용해야 합니다.

**Do**:

```dart
final provider = Provider<String>((ref) => 'Hello world');
```

**Don't**:

```dart
class Example {
  // 지원되지 않는 작업입니다. 메모리 누수 및 예기치 않은 동작이 발생할 수 있습니다.
  final provider = Provider<String>((ref) => 'Hello world');
}
```

:::info
provider를 static final 변수로 생성하는 것은 허용되지만, 코드 생성기에서는 지원되지 않습니다.
:::
